package ProcessUtils

import (
	"errors"
	"gitee.com/fierce_wolf/go-fox-edge-common/commEnv"
	"gitee.com/fierce_wolf/go-fox-edge-common/commUtil/FileName"
	"gitee.com/fierce_wolf/go-fox-edge-common/commUtil/Map"
	"gitee.com/fierce_wolf/go-fox-edge-common/commUtil/Method"
	"gitee.com/fierce_wolf/go-fox-edge-common/commUtil/Number"
	"gitee.com/fierce_wolf/go-fox-edge-common/commUtil/OS/ShellUtils"
	"gitee.com/fierce_wolf/go-fox-edge-common/commUtil/Slice"
	"gitee.com/fierce_wolf/go-fox-edge-common/commUtil/String"
	"strconv"
	"strings"
)

func ExtendAppStatus(serviceIniFileInfoList []map[string]interface{}) error {
	processList, err := GetProcessAll()
	if err != nil {
		return err
	}

	pathName2ProcessStatus := Map.BuildMapByKey(processList, "pathName")

	for _, confFileInfo := range serviceIniFileInfoList {
		pathName := confFileInfo["pathName"]
		if Method.HasEmpty(pathName) {
			continue
		}

		status := pathName2ProcessStatus[pathName]
		if status != nil {
			Map.Append(confFileInfo, status.(map[string]interface{}))
		}
	}

	return nil
}

func GetProcessAll() ([]map[string]interface{}, error) {
	resultList := make([]map[string]interface{}, 0)

	list, err := getProcessAppType("kernel")
	if err != nil {
		return nil, err
	}
	resultList = Slice.Append(resultList, list)

	list, err = getProcessAppType("system")
	if err != nil {
		return nil, err
	}
	resultList = Slice.Append(resultList, list)

	list, err = getProcessAppType("service")
	if err != nil {
		return nil, err
	}
	resultList = Slice.Append(resultList, list)

	return resultList, nil
}

func getProcessAppType(appType string) ([]map[string]interface{}, error) {
	resultList := make([]map[string]interface{}, 0)

	path := FileName.GetOsFilePath(commEnv.Service.WorkDir + "/bin/" + appType + "/")

	shellLineList, err := ShellUtils.ExecuteShell("ps -aux|grep " + path)
	if err != nil {
		return nil, err
	}

	for _, shellLine := range shellLineList {
		items := strings.Fields(shellLine)
		if len(items) < 18 {
			continue
		}

		// 剔除掉DEBUG的干扰信息
		items = filterJavaDebug(items)
		items = filterGoDebug(items)

		// ps -aux返回的格式: 0~10是linux的固定信息项目
		data := makeShellParam(items)

		// 从命令行信息中取出 用户参数
		params := splitLinuxShellParam(items)

		// 从fox-edge格式的用户参数中，取出用户参数
		foxParam := splitFoxEdgeParam(params, path)
		Map.Append(data, foxParam)

		// 确认引擎类型
		appEngine := items[10]
		_, ok := foxParam["appEngine"]
		if ok {
			appEngine = Map.GetOrDefault(foxParam, "appEngine", "java").(string)
		}
		if "java" != appEngine && "python" != appEngine && "python3" != appEngine && "native" != appEngine {
			continue
		}

		data["appType"] = appType
		data["appEngine"] = appEngine

		resultList = append(resultList, data)
	}

	return resultList, nil
}

func filterJavaDebug(items []string) []string {
	list := make([]string, 0)
	for _, item := range items {
		// 过滤掉idea的调试信息
		if strings.HasPrefix(item, "-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=") {
			continue
		}

		list = append(list, item)
	}

	return list
}

func filterGoDebug(items []string) []string {
	if len(items) < 18 {
		return items
	}

	if items[10] != "dlv" || items[15] != "exec" || items[17] != "--" {
		return items
	}

	list := make([]string, 0)
	for i := 0; i < 10; i++ {
		list = append(list, items[i])
	}
	list = append(list, items[16])
	for i := 18; i < len(items); i++ {
		list = append(list, items[i])
	}

	return list
}

func makeShellParam(items []string) map[string]interface{} {
	// ps -aux返回的格式
	// 0~10是linux的固定信息项目
	data := make(map[string]interface{})
	data["user"] = items[0]
	data["pid"] = Number.ParseInt64(items[1])
	data["cpu"] = Number.ParseFloat64(items[2])
	data["men"] = Number.ParseFloat64(items[3])
	data["vsz"] = Number.ParseInt64(items[4])
	data["rss"] = Number.ParseInt64(items[5])
	data["stime"] = items[8]
	data["time"] = items[9]
	data["command"] = items[10]

	return data
}

func splitLinuxShellParam(items []string) []string {
	params := make([]string, 0)
	for i := 10; i < len(items); i++ {
		params = append(params, items[i])
	}

	return params
}

func splitFoxEdgeParam(params []string, path string) map[string]interface{} {
	result := make(map[string]interface{})
	if len(params) < 3 {
		return result
	}

	// 12 /opt/fox-edge/bin/xxx/xxxx 例如，/opt/fox-edge/bin/service/fox-edge-server-proxy-redis-topic-service-1.0.0.jar
	// 分解出路径名称和文件名称
	appNameAndFileName := findParam(params, path)
	if appNameAndFileName != "" {
		pathName := path + appNameAndFileName
		result["pathName"] = pathName

		items := strings.Split(pathName, "/")
		if len(items) > 0 {
			result["fileName"] = items[len(items)-1]
		}
	}

	appEngine := findParam(params, "--app_engine=")
	appName := findParam(params, "--app_name=")
	springRedisHost := findParam(params, "--spring.redis.host=")
	springRedisPort := findParam(params, "--spring.redis.port=")
	serverPort := findParam(params, "--server.port=")

	// java和python/native的差异
	if springRedisHost == "" {
		springRedisHost = findParam(params, "redis.host=")
	}
	if springRedisPort == "" {
		springRedisPort = findParam(params, "redis.port=")
	}
	if serverPort == "" {
		serverPort = findParam(params, "server.port=")
	}

	if appEngine != "" {
		result["appEngine"] = appEngine
	}
	if appName != "" {
		result["appName"] = appName
	}
	if springRedisHost != "" {
		result["redisHost"] = springRedisHost
	}
	if springRedisPort != "" {
		result["redisPort"] = springRedisPort
	}
	if serverPort != "" {
		appPort := Number.ParseInt32(serverPort)
		result["appPort"] = appPort
	}

	return result
}

func findParam(shellParams []string, springHeader string) string {
	for _, shellParam := range shellParams {
		if !strings.HasPrefix(shellParam, springHeader) {
			continue
		}

		return shellParam[len(springHeader):]
	}

	return ""
}

func FindPidByPort(appPort int) int64 {
	shellLineList, err := ShellUtils.ExecuteShell("netstat -anp | grep " + String.ToString(appPort))
	if err != nil {
		return 0
	}

	for _, shellLine := range shellLineList {
		items := strings.Fields(shellLine)
		if len(items) != 7 {
			continue
		}

		ipAndPort := items[3]
		pidAndCmd := items[6]

		portItems := strings.Split(ipAndPort, ":")

		// 检查：是否为指定的端口
		if len(portItems) > 0 && portItems[len(portItems)-1] == String.ToString(appPort) {
			pidItems := strings.Split(pidAndCmd, "/")
			if len(pidItems) > 0 {
				id, err := strconv.ParseInt(pidItems[0], 10, 64)
				if err != nil {
					continue
				}

				return id
			}
		}
	}

	return 0
}

func GetSysProcess(feature string, isSysParam bool) (map[string]interface{}, error) {
	shellLineList, err := ShellUtils.ExecuteShell("ps -aux|grep " + feature)
	if err != nil {
		return nil, err
	}

	for _, shellLine := range shellLineList {
		items := strings.Fields(shellLine)
		if len(items) < 11 {
			continue
		}

		// 剔除掉DEBUG的干扰信息
		items = filterJavaDebug(items)
		items = filterGoDebug(items)

		// ps -aux返回的格式: 0~10是linux的固定信息项目
		data := makeShellParam(items)

		// 检查：是否存在该特征
		if isSysParam {
			command := Map.GetString(data, "command", "")
			if strings.HasSuffix(command, feature) {
				return data, nil
			}
		} else {
			// 非系统参数部分
			for _, item := range items {
				if strings.Contains(item, feature) {
					return data, nil
				}
			}
		}
	}

	return nil, errors.New("找不到指定的进程")
}

func GetProcessPort(pid int64) ([]int, error) {
	shellLineList, err := ShellUtils.ExecuteShell("ss -tnlp | grep pid=" + String.ToString(pid))
	if err != nil {
		return nil, err
	}

	ports := make(map[int]interface{})
	for _, shellLine := range shellLineList {
		items := strings.Fields(shellLine)
		if len(items) != 6 {
			continue
		}

		// 分拆IP:PORT
		items = strings.Split(items[3], ":")
		if len(items) < 2 {
			continue
		}

		val, err := strconv.ParseInt(items[len(items)-1], 10, 64)
		if err != nil {
			return nil, err
		}

		ports[int(val)] = nil
	}

	keys := make([]int, 0)
	for key, _ := range ports {
		keys = append(keys, key)
	}

	return keys, nil
}

func GcProcess(pid int64) error {
	_, err := ShellUtils.ExecuteShell("jmap -histo:live " + String.ToString(pid) + " | head -10")
	return err
}
